#include <GameEngine/UnitTest/unit_test.h>

#include <GameEngine/Geometry/Surfaces/hexagon2d.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//! Launch the unit tests on Hexagon2d objects
/*!
 @return True if all unit tests on Hexagon2d objects were
         succesful, false otherwise
 */
template <class T>
bool UnitTest::test_geometry_hexagon2d_functions() {
	bool res = true;

	// Get number of bytes
	MemoryManager::BytesCount bytes = MemoryManager::instance()->bytes();

	// Create hexagon
	GameEngine::Point2d<T> center(0,0);
	GameEngine::Vector2d<T> orientation(1,0);
	GameEngine::Hexagon2d<T> hex(center,orientation);

	// Test accessor functions
	T tolerance = (T)0.00001;
	{{
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test center()",  ( center.get_distance(hex.center()) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test orientation()", ( orientation.get_distance(hex.orientation()) < tolerance ) );
	}}

	// Test operator[] function
	{{
		double distances[] = {
			hex[Hexagon2D::First].get_distance(hex[Hexagon2D::Second]),
			hex[Hexagon2D::Second].get_distance(hex[Hexagon2D::Third]),
			hex[Hexagon2D::Third].get_distance(hex[Hexagon2D::Fourth]),
			hex[Hexagon2D::Fourth].get_distance(hex[Hexagon2D::Fifth]),
			hex[Hexagon2D::Fifth].get_distance(hex[Hexagon2D::Sixth]),
			hex[Hexagon2D::Sixth].get_distance(hex[Hexagon2D::First])
		};
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test operator[](First,Second)",  ( abs( hex.orientation().length() - distances[0] ) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test operator[](Second,Third)", ( abs( hex.orientation().length() - distances[1] ) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test operator[](Third,Fourth)",  ( abs( hex.orientation().length() - distances[2] ) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test operator[](Fourth,Fifth)", ( abs( hex.orientation().length() - distances[3] ) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test operator[](Fifth,Sixth)",  ( abs( hex.orientation().length() - distances[4] ) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test operator[](Sixth,First)",  ( abs( hex.orientation().length() - distances[5] ) < tolerance ) );
	}}

	// Test inside function
	{{
		GameEngine::Point2d<T> p0((T)0.5,(T)0);
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test inside()", ( hex.inside(p0) == true ) );
		GameEngine::Point2d<T> p1((T)1,(T)0);
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test inside()", ( hex.inside(p1,false) == true ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test inside()", ( hex.inside(p1,true) == false ) );
		GameEngine::Point2d<T> p2((T)6,(T)1);
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test inside()", ( hex.inside(p2) == false ) );
	}}

	// Test line intersection function
	{{
		// Create a test hexaedron
		GameEngine::Hexagon2d<T> hex_test(center, GameEngine::Vector2d<T>(orientation.x()+tolerance,0) );

		// Create line that crosses the box
		GameEngine::Line2d<T> line0(GameEngine::Point2d<T>((T)-1,(T)-1), GameEngine::Point2d<T>((T)1,(T)2));
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		int nb = hex.intersection(line0, coord1, coord2, &type);
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test intersection(line)", ( nb == 2 ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test intersection(line)", ( type == GameEngine::IntersectionType::Intersection ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test intersection(line)", ( hex_test.inside(line0.point(coord1)) == true ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test intersection(line)", ( hex_test.inside(line0.point(coord2)) == true ) );

		// Create line that does not cross the box
		GameEngine::Line2d<T> line1(GameEngine::Point2d<T>((T)-2,(T)-1), GameEngine::Point2d<T>((T)-2,(T)2));
		nb = hex.intersection(line1, coord1, coord2, &type);
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test intersection(line)", ( nb == 0 ) );
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test intersection(line)", ( type == GameEngine::IntersectionType::NoIntersection ) );
	}}

	// Test closest_point function
	{{
		// Create outside point
		GameEngine::Point2d<T> p0((T)-1.5,(T)0);
		GameEngine::Point2d<T> cp0 = hex.closest_point(p0);
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test closest_point()", ( cp0.get_distance( GameEngine::Point2d<T>((T)-1,(T)0) ) < tolerance ) );

		// Create outside point
		GameEngine::Point2d<T> p1((T)0,(T)-1.3);
		GameEngine::Point2d<T> cp1 = hex.closest_point(p1);
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test closest_point()", ( cp1.get_distance( GameEngine::Point2d<T>((T)0,(T)(-sqrt(3.0)/2.0)) ) < tolerance ) );
	}}

	// Test perimeter function
	{{
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test perimeter()", ( abs(hex.perimeter()-(T)6) < tolerance ) );
	}}

	// Test area function
	{{
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test area()", ( abs(hex.area()-(T)(3.0*sqrt(3.0)/2.0)) < tolerance ) );
	}}

	// Test degenerate function
	{{
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test degenerate()", ( hex.degenerate() == false ) );
		GameEngine::Hexagon2d<T> hex0(center, Vector2d<float>(0,0));
		res &= UNIT_TEST_ERROR( "[Hexagon2d] Test degenerate()", ( hex0.degenerate() == true ) );
	}}

	// Get number of bytes
	res &= UNIT_TEST_ERROR( "[Hexagon2d] Test memory leak", ( bytes == MemoryManager::instance()->bytes() ));

	// Return result
	return res;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
